<?php

 /**
  * @package JooS
  * @subpackage Syntax
  */

  require_once "JooS/Loader.php";
  
  final class JooS_Syntax_Compiler {
    /**
     * @var JooS_Syntax_Abstract
     */
    private $_syntax = null;

    /**
     * @var string
     */
    private $_expr = null;

    /**
     * @var int
     */
    private $_exprPos = null;

    /**
     * @var int
     */
    private $_exprLength = null;

    /**
     * @param JooS_Syntax_Abstract $syntax
     * @return JooS_Syntax_Compiler
     */
    public static function newInstance(JooS_Syntax_Abstract $syntax, $a1 = null, $a2 = null, $a3 = null) {
      $instance = new self($syntax);

      $syntax->setParser($instance);
      $syntax->init($a1, $a2, $a3);

      return $instance;
    }

    /**
     * @param JooS_Syntax_Abstract $syntax
     */
    private function __construct(JooS_Syntax_Abstract $syntax) {
      $this->_syntax = $syntax;
    }

    /**
     * @param string|JooS_Syntax_Abstract $name
     * @return JooS_Syntax_Abstract
     */
    public function createSyntax($name, $a1 = null, $a2 = null, $a3 = null) {
      $className = JooS_Loader::getClassName("JooS_Syntax", $name);

      if (JooS_Loader::loadClass($className)) {
        $syntax = new $className();

        if ($syntax instanceOf JooS_Syntax_Abstract) {
          $syntax->setParser($this);
          $syntax->init($a1, $a2, $a3);
          return $syntax;
        }
        else {
          require_once "JooS/Syntax/Exception.php";
          throw new JooS_Syntax_Exception("Class must be instance of JooS_Syntax_Abstract");
        }
      }
      else {
        require_once "JooS/Syntax/Exception.php";
        throw new JooS_Syntax_Exception("Syntax class not found");
      }
    }

    /**
     * @param string $expr
     * @return bool
     */
    public function compile($expr) {
      $this->_expr = $expr;
      $this->_exprPos = 0;
      $this->_exprLength = strlen($expr);

      $compiled = $this->_syntax->compileSyntax();
      
      return $this->_exprPos === $this->_exprLength ? $compiled : false;
    }

    /**
     * @param int $count
     * @return string
     */
    public function substr($count) {
      switch (true) {
        case $count == 0:
          return "";
        case $this->_exprPos + $count <= $this->_exprLength:
          return substr($this->_expr, $this->_exprPos, $count);
        default:
          return false;
      }
    }

    /**
     * @return int
     */
    public function getPos() {
      return $this->_exprPos;
    }

    /**
     * @param int $exprPos
     */
    public function setPos($exprPos) {
      $this->_exprPos = $exprPos;
    }

    /**
     * @param int $count
     */
    public function shiftPos($count) {
      $this->_exprPos += $count;
    }

    private static $_phpReservedWords = array(
      "abstract", "and", "array", "as", "break", "case", "catch", "class", "clone", "const",
      "continue", "declare", "default", "do", "else", "elseif", "enddeclare", "endfor",
      "endforeach", "endif", "endswitch", "endwhile", "extends", "final", "for", "foreach",
      "function", "global", "goto", "if", "implements", "interface", "instanceof", "namespace",
      "new", "or", "private", "protected", "public", "static", "switch", "throw", "try", "use",
      "var", "while", "xor",
      "__CLASS__", "__DIR__", "__FILE__", "__FUNCTION__", "__METHOD__", "__NAMESPACE__",
      "die", "echo", "empty", "exit", "eval", "include", "include_once", "isset", "list",
      "require", "require_once", "return", "print", "unset",
    );

    public static function isReservedWord($word) {
      return in_array($word, self::$_phpReservedWords);
    }
  }
